/*
 * ads_mgr.h
 *
 *  Created on: Dec 21, 2012
 *      Author: wangmu
 */

#ifndef ADS_MGR_H_
#define ADS_MGR_H_


#include <list>
#include <vector>
#include <string>
#include <algorithm>
#include <tr1/memory>
#include "csapp.h"
#include "xml_utils.h"
#define MAXEVENTS 65536
using namespace std;
#define MIN_TIME_INTERVAL 10 // 10 sec

/**
 * used for convert string between int/double/float
 *
 */
template<class out_type,class in_value>
out_type str_convert(const in_value & t)
{
	stringstream stream;
	stream<<t;//鍚戞祦涓紶鍊�
	out_type result;//杩欓噷瀛樺偍杞崲缁撴灉
	stream>>result;//鍚憆esult涓啓鍏ュ�
	return result;
}

/**
 * ads_service_type
 * define service type
 */
enum ads_service_type
{
	ADS_SERVICE_UNSUPPORT = 0x01,	// service un-support
	ADS_SERVICE_ONCE	= 0x02,			// send data once
	ADS_SERVICE_ONCHANGED =0x04,		// send data when changed
	ADS_SERVICE_PERIOD	= 0x08,			// send data period with interval time
	ADS_SERVICE_STOP = 0x16,	// service stop
};

/*
 * define service name
 */
#define ADS_SERVICE_ALMANAC				"Almanac"
#define ADS_SERVICE_UTCModel    		"UTCModel"
#define ADS_SERVICE_IonosphericModel	"IonosphericModel"
#define ADS_SERVICE_NavigationModel		"NavigationModel"
#define ADS_SERVICE_DGPSCorrections		"DGPSCorrections"
#define ADS_SERVICE_ReferenceTime		"ReferenceTime"
#define ADS_SERVICE_RealTimeIntegrity	"RealTimeIntegrity"

#define ADS_SUBSCRIBE_STR 				"Subscribe"
#define ADS_UN_SUBSCRIBE_STR 			"UnSubscribe"

/**
 * service_item_enable
 */
typedef struct service_item_enable
{
	char *service_name;
	bool enable;
};

#define MAX_GPS_SERVICE_NUMBER 7


/**
 * define user state
 */
enum User_State{
	ADS_USER_INIT = 0,		// init state or socket open but nothing received
	ADS_USER_CONNECTED,			// fd connected
	ADS_USER_RECVD_SUB,		// received subscribe request
	ADS_USER_SENT_SUB_RES,  // sent subscribe response
	ADS_USER_RECVD_DATA_REQ, // received data request
	ADS_USER_SEND_DATA_REQ_RES,  // sent data request response
	ADS_USER_SEND_ASSIST_DATA, // sent assist data
};

#define USER_STATE_STR(state)		\
		(state == ADS_USER_INIT) 			    ? "ADS_USER_INIT":\
		(state == ADS_USER_CONNECTED)		    ?"ADS_USER_CONNECTED":\
		(state == ADS_USER_RECVD_SUB)		    ?"ADS_USER_RECVD_SUB":\
		(state == ADS_USER_SENT_SUB_RES)    	?"ADS_USER_SENT_SUB_RES":\
		(state == ADS_USER_RECVD_DATA_REQ)     	?"ADS_USER_RECVD_DATA_REQ":\
		(state == ADS_USER_SEND_DATA_REQ_RES) 	?"ADS_USER_SEND_DATA_REQ_RES":\
		(state == ADS_USER_SEND_ASSIST_DATA) 		?"ADS_USER_SEND_ASSIST_DATA":\
		"UN_KNOWN_USER_STATE"

class IUserObserver{
public:
	virtual int ads_user_notify(int code, string user_id, string user_pwd) = 0;
};

/**
 * ads service struct
 */
typedef struct ads_service_struct{
	string service_name;
	ads_service_type type;
	int interval;		// record the interval
	struct timespec time_t; // record the last time that data has been sent. w
							// will be update to now after new sent
	int data_sent; // record how many time this service has been sent
}ads_service_struct;

/**
 * CAdsUser
 */
class CAdsUser{
public:
	CAdsUser(const IUserObserver& ob);
	void set_fd(int fd){m_fd = fd;};
	int get_fd(){return m_fd;};
	int get_state(){return m_state;}
	int get_send_bytes() {return m_sent_bytes;};
	int get_recv_bytes(){return m_recv_bytes;};
	int get_total_tick() {return m_total_tick;};
	int get_last_sent_tick() {return m_last_sent_tick;};
	int get_last_recv_tick() {return m_last_recv_tick;};

	void set_seq_number(int number){
		m_seq_number = number;
	};
	void set_state(User_State state) {
		m_state = state;
	}

	int do_ads_action();
	int send_xml_assist_data();
	//int get_fd(){return m_fd;}
	//int get_seq_number(){return m_seq_number;};
	//User_State get_state(){return m_state;};
	~CAdsUser();
private:
	void clear_state();

	int ads_do_sub_req(const string& str);
	int ads_do_data_req(const string &str);
	int send_xml_data(const string& str);
	int send_file(string filename);
	int action_on_subscribe(const string& xmlstr);
	int action_on_unscribe_or_data_req(const string& xmlstr); // may be unscribe, data_quest
	int action_on_data_request(const string& xmlstr);
	int do_action_on_data_request(const data_request_gps& req_gps);
	int read_socket(string &xmlbuffer);
	int do_read_assist_data(gps_assist_data& gad_out);
	void set_assist_data_out(gps_assist_data &out_data, string name, bool enable);
private:
	int m_fd; //socket fd
	User_State m_state; // CAdsUser state
	int m_seq_number;  // increase by 1 after send a "new" message
	int m_req_number; // recv from client
	string m_user_id; // user name
	string m_user_pswd;// user passwd
	int m_sent_bytes; // total sent bytes
	int m_recv_bytes; // total recv bytes
	int m_total_tick;
	int m_last_sent_tick;
	int m_last_recv_tick;

	ref_location m_ref_l;		// data request reference location
	//used for record interval. if interval reached, send out assist data
	//and refresh the time
	//every MIN_TIME_INTERVALs will update this
	//services_list will take the user request
	vector<ads_service_struct> m_services_list; // config data list
	//m_gps_srv_en_lst will record the user request and disable the serivce user not request
	service_item_enable m_gps_srv_en_lst[MAX_GPS_SERVICE_NUMBER]; // gps service item enable/disable list


	const IUserObserver& m_observer;
	sem_t m_act_lck;       /* lock the user */
	string m_recv;
};
typedef tr1::shared_ptr<CAdsUser> ads_ptr;
typedef vector<ads_ptr> vector_ads_type;

class CUserMgr:public IUserObserver{
public:
	CUserMgr(int max_users = MAXEVENTS);
	ads_ptr get_user(int fd);
	virtual int ads_user_notify(int code, string user_id, string user_pwd){return 0;};
	// main test function
	int trigger_test();
	User_State get_user_state(int fd);
	virtual ~CUserMgr(){};
	int show_statistics(bool b_detail);
private:
	//socket keep live users
	vector_ads_type m_on_line_users;
	//for the user off line
	std::list<CAdsUser> m_off_line_users;
	sem_t m_on_line_lck;       /* lock the user */

};


#endif /* ADS_MGR_H_ */
